package heap

import "gvm/classfile"

type Method struct {
	ClassMember
	// 参数曹
	argSlotCount uint
	// 最大栈深度
	maxStack uint
	// 最大局部变量个数
	maxLocals uint
	// 字节码指令
	code []uint8
	// 异常表
	exceptionTable ExceptionTable
	// 行表
	lineNumberTable         *classfile.LineNumberTableAttr
	parameterAnnotationData []uint8 // RuntimeVisibleParameterAnnotations_attribute
	annotationDefaultData   []uint8 // AnnotationDefault_attribute
	parseDescriptor         *MethodDescriptor
}

//func (self *MethodDescriptor) addParameterType(t string) {
//	//plen := len(self.parameterTypes)
//	//if plen == cap(self.parameterTypes){
//	//	s:=make([]string,plen,plen+4)
//	//	copy(s,self.parameterTypes)
//	//	self.parameterTypes=s
//	//}
//	if self.parameterTypes == nil {
//		self.parameterTypes = make([]string, 0)
//	}
//	self.parameterTypes = append(self.parameterTypes, t)
//}

func (m *Method) copyAttributes(method *classfile.MemberInfo) {
	if codeAttr := method.CodeAttribute(); codeAttr != nil {
		m.maxStack = uint(codeAttr.MaxStack())
		m.maxLocals = uint(codeAttr.MaxLocals())
		m.code = codeAttr.Code()
		m.exceptionTable = newExceptionTable(codeAttr.ExceptionTable(), m.class.constantPool)
		m.lineNumberTable = codeAttr.LineNumberTableAttribute()
	}
}

func (m *Method) calcArgSlotCount(paramTypes []string) {
	for _, paramType := range paramTypes {
		m.argSlotCount++
		if paramType == "J" || paramType == "D" {
			m.argSlotCount++
		}
	}
	if !m.IsStatic() {
		m.argSlotCount++
	}
}

func newExceptionTable(tables []classfile.ExceptionTable, cp *ConstantPool) ExceptionTable {
	table := make([]*ExceptionHandler, len(tables))
	for i, entry := range tables {
		table[i] = &ExceptionHandler{
			startPc:   int(entry.StartPc()),
			endPc:     int(entry.EndPc()),
			handlerPc: int(entry.HandlerPc()),
			catchType: getCatchType(uint(entry.CatchType()), cp),
		}
	}
	return table
}

func getCatchType(index uint, cp *ConstantPool) *ClassRef {
	if index == 0 {
		return nil
	}
	return cp.GetConstant(index).(*ClassRef)
}

func (m *Method) injectCodeAttribute(returnType string) {
	m.maxStack = 4
	m.maxLocals = m.argSlotCount
	switch returnType[0] {
	case 'V':
		m.code = []byte{0xfe, 0xb1} //return
	case 'D':
		m.code = []byte{0xfe, 0xaf} //dreturn
	case 'F':
		m.code = []byte{0xfe, 0xae} //freturn
	case 'J':
		m.code = []byte{0xfe, 0xad} //lreturn
	case 'L', '[':
		m.code = []byte{0xfe, 0xb0} //areturn
	default:
		m.code = []byte{0xfe, 0xac} // ireturn
	}
}

func (m *Method) MaxLocals() uint {
	return m.maxLocals
}

func (m *Method) MaxStack() uint {
	return m.maxStack
}

func (m *Method) ArgSlotCount() uint {
	return m.argSlotCount
}

func (m *Method) Code() []byte {
	return m.code
}

func (m *Method) GetLineNumber(pc int) int {
	if m.IsNative() {
		return -2
	}
	if m.lineNumberTable == nil {
		return -1
	}
	return m.lineNumberTable.GetLineNumber(pc)
}

func (m *Method) FindExceptionHandler(exClass *Klass, pc int) int {
	handler := m.exceptionTable.findExceptionHandler(exClass, pc)
	if handler != nil {
		return handler.handlerPc
	}
	return -1
}

func NewMethods(class *Klass, cfMethods []*classfile.MemberInfo) []*Method {
	methods := make([]*Method, len(cfMethods))
	for i, cfMethod := range cfMethods {
		methods[i] = &Method{}
		methods[i].class = class
		methods[i].copyMemberInfo(cfMethod)
		methods[i].copyAttributes(cfMethod)

		md := parseMethodDescriptor(methods[i].descriptor)
		methods[i].parseDescriptor = md
		methods[i].calcArgSlotCount(md.parameterTypes)
		if methods[i].IsNative() {
			methods[i].injectCodeAttribute(md.returnType)
		}
	}

	return methods
}

type ExceptionHandler struct {
	startPc   int
	endPc     int
	handlerPc int
	catchType *ClassRef
}
type ExceptionTable []*ExceptionHandler

func (self ExceptionTable) findExceptionHandler(exClass *Klass, pc int) *ExceptionHandler {
	for _, handler := range self {
		if pc >= handler.startPc && pc < handler.endPc {
			if handler.catchType == nil {
				return handler //catch-all
			}

			catchClass := handler.catchType.ResolveClass()
			if catchClass == exClass || catchClass.IsSuperClassOf(exClass) {
				return handler
			}
		}
	}
	return nil
}
